package net.sansi.v3correctserver.entity.image;

import cn.hutool.core.io.FileUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.entity.color.PhotoColor;
import net.sansi.v3correctserver.exception.ServiceException;
import net.sansi.v3correctserver.util.CmmUtil;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.libraw.LibRaw;
import org.bytedeco.libraw.libraw_output_params_t;
import org.bytedeco.libraw.libraw_processed_image_t;

import java.io.File;
import java.lang.ref.Cleaner;
import java.nio.file.Path;

import static org.bytedeco.libraw.global.LibRaw.libraw_strerror;

/**
 * Created by Fang
 * create time : 2024/11/19
 * IDE : IntelliJ IDEA
 * <p>
 * Raw 图片实现（Cr2, Cr3）
 */
@Slf4j
@Getter
public class RecRawImage extends RecImageBase {
    LibRaw rawProcessor;
    private static final Cleaner cleaner = Cleaner.create();
    RecImage blackPhoto;
    int outputColor = 0;

    public RecRawImage(Path path) {
        this(path, null, null);
    }


    public RecRawImage(Path path, RecImage blackPhoto) {
        this(path, blackPhoto, null);
    }


    int width;
    int height;
    int colorSize;
    int byteCount;
    libraw_processed_image_t it;
    File tempFile;

    public RecRawImage(Path path, RecImage blackPhoto, SetProcessParams params) {
        setPath(path);
        this.blackPhoto = blackPhoto;
        try {
            tempFile = CmmUtil.createTempFile();
            FileUtil.copy(path.toFile(), tempFile, true);
            path = tempFile.toPath();
            rawProcessor = new LibRaw();
            cleaner.register(this, new RecRawImageCleaner(rawProcessor, tempFile));
            if (params == null) {
                defaultParams(rawProcessor.imgdata().params());
            } else {
                params.params(rawProcessor.imgdata().params());
            }
            int ret = rawProcessor.open_file(path2String(path));
            handleError(ret, "无法打开 raw 文件 " + path);
            ret = rawProcessor.unpack();
            handleError(ret, "无法解码 raw 文件 " + path);
            ret = rawProcessor.dcraw_process();
            handleError(ret, "无法处理 raw 文件 " + path);
            it = rawProcessor.dcraw_make_mem_image();
            width = it.width();
            height = it.height();
            int size = it.data_size();
            colorSize = size / (width * height);
            byteCount = colorSize / 3;
        } catch (Exception e) {
            throw new ServiceException("raw 文件解析失败！ {}", e.getMessage(), e);
        }
    }

    public int byte2Color(libraw_processed_image_t it, int start, int len) {
        int res = 0;
        for (int i = start + len - 1; i >= start; i--) {
            res = (res << 8) | (it.data(i) & 0xFF);
        }
        return res;
    }

    public static void handleError(int err, String message) {
        if (err != org.bytedeco.libraw.global.LibRaw.LibRaw_errors.LIBRAW_SUCCESS.value) {
            final String msg;
            try (BytePointer e = libraw_strerror(err)) {
                msg = e.getString();
            }
            throw new ServiceException("raw文件读写错误！ {} {} {}", message, err, msg);
        }
    }

    @Override
    public int getWidth() {
        return width;
    }

    @Override
    public int getHeight() {
        return height;
    }

    /**
     * NTSC 亮度计算
     * Y = 0.299R + 0.587G + 0.114B
     *
     * @param x x
     * @param y y 坐标
     * @return
     */
    @Override
    public double getBrightness(int x, int y) {
        if (outputColor == 5) return getPhotoColor(x, y).getG();
        PhotoColor color = getPhotoColor(x, y);
        return color.getNTSCBrightness();
        //return super.getBrightness(x, y);
    }

    @Override
    public PhotoColor getPhotoColor(int x, int y) {
        int p = (y * width + x) * colorSize;
        int r = byte2Color(it, p, byteCount);
        int g = byte2Color(it, p + byteCount, byteCount);
        int b = byte2Color(it, p + byteCount + byteCount, byteCount);
        PhotoColor pc = new PhotoColor(r, g, b);
        if (blackPhoto != null) pc = pc.minus(blackPhoto.getPhotoColor(x, y));
        return pc;
    }

    public interface SetProcessParams {
        void params(libraw_output_params_t params);
    }

    /**
     * 设置 raw 文件处理参数
     */
    private void defaultParams(libraw_output_params_t params) {
        params.output_color(outputColor);
        params.output_bps(16);
        params.half_size(1); // Create half size image
        //params.output_tiff(1); // Save as TIFF
        params.gamm(0, 1);
        params.gamm(1, 1);
        params.no_auto_bright(1);
        params.use_camera_matrix(0);
        params.use_camera_wb(0);
        params.user_sat(0);
        params.aber(0, 1);
        params.aber(1, 1);
        params.aber(2, 1);
        params.aber(3, 1);
        //params.med_passes(1);
        params.fbdd_noiserd(1);
    }

    private record RecRawImageCleaner(LibRaw rawProcessor, File file) implements Runnable {

        @Override
        public void run() {

            log.info("回收相片内存！ {}", file.toPath().toAbsolutePath());
            if (rawProcessor != null) {
                rawProcessor.recycle();
                rawProcessor.close();
            }
            if (!file.delete()) {
                log.error("删除文件失败 {}", file.getAbsoluteFile());
            }
        }
    }

    /**
     * 转换成 CmmImage 图片
     *
     * @param exp 指数
     * @return CmmImage
     */
    public RecCmmImage toCmmImage(double exp) {
        int w = this.getWidth(), h = this.getHeight();
        RecCmmImage res = new RecCmmImage(w, h);
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                PhotoColor color = this.getPhotoColor(x, y);
                res.setColor(x, y, color.toExp(exp));
            }
        }
        return res;
    }

}
